﻿namespace NTLib.Application.Themes
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
    using NTLib.Core.Threading;
    using Core.Reflection;
    
    /// <summary>
    /// 
    /// </summary>
    public static class ThemeManager
    {
        #region Fields

        public const string DEFAULT = ObjectDescriptor.NULL_STR;

        private static readonly Dictionary<Guid, IThemeDictionary> s_themes;
        private static readonly ReaderWriterLockSlim s_locker;
        private static readonly List<IResourceDictionary> s_lastResources;
        private static string s_currentTheme = null;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes the <see cref="ThemeManager"/> class.
        /// </summary>
        static ThemeManager()
        {
            s_themes = new Dictionary<Guid, IThemeDictionary>();
            s_locker = new ReaderWriterLockSlim();
            s_lastResources = new List<IResourceDictionary>();
        }

        #endregion

        #region Event

        /// <summary>
        /// Raised when the theme changed.
        /// </summary>
        public static Action ThemeChanged;
        private static ICollection<IResourceDictionary> s_dictionaryRoot;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the registered themes.
        /// </summary>
        public static IEnumerable<string> Themes
        {
            get
            {
                using (s_locker.LockRead())
                {
                    return s_themes.Select(s =>
                    {
                        if (string.IsNullOrEmpty(s.Value.ThemeName))
                            return DEFAULT;
                        return s.Value.ThemeName.ToUpperInvariant();
                    }).Distinct();
                }
            }
        }

        /// <summary>
        /// Gets the current theme.
        /// </summary>
        public static string CurrentTheme
        {
            get
            {
                using (s_locker.LockRead())
                {
                    return s_currentTheme;
                }
            }
        }

        /// <summary>
        /// Gets or sets the dictionary root.
        /// </summary>
        public static ICollection<IResourceDictionary> DictionaryRoot
        {
            get
            {
                using (s_locker.LockRead())
                    return s_dictionaryRoot;
            }
            set
            {
                using (s_locker.LockWrite())
                {
                    if (s_dictionaryRoot != value)
                    {
                        s_dictionaryRoot = value;
                    }
                }
                if (s_dictionaryRoot != null)
                {
                    var theme = s_currentTheme;
                    s_currentTheme = null;
                    if (string.IsNullOrEmpty(theme))
                        theme = DEFAULT;
                    SetThemeAsync(theme);
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Registers the theme.
        /// </summary>
        /// <param name="theme">The theme.</param>
        public static void RegisterTheme(IThemeDictionary theme)
        {
            using (s_locker.LockWrite())
            {
                if (s_themes.ContainsKey(theme.Id))
                    return;
                s_themes.Add(theme.Id, theme);
                if (string.IsNullOrEmpty(theme.ThemeName) &&
                        string.IsNullOrEmpty(s_currentTheme) && s_dictionaryRoot != null)
                {
                    DispatchHelper.Instance.DispatchExec(() =>
                    {
                        if (theme.Dictionary != null)
                        {
                            s_dictionaryRoot.Add(theme.Dictionary);
                            if (!s_lastResources.Contains(theme.Dictionary))
                                s_lastResources.Add(theme.Dictionary);
                        }
                    });
                }
            }
        }

        /// <summary>
        /// Unregister theme.
        /// </summary>
        /// <param name="theme">The theme.</param>
        public static void UnRegisterTheme(IThemeDictionary theme)
        {
            using (s_locker.LockWrite())
            {
                if (s_themes.ContainsKey(theme.Id))
                {
                    s_themes.Remove(theme.Id);
                    DispatchHelper.Instance.DispatchExec(() =>
                    {
                        if (theme.Dictionary != null)
                        {
                            s_dictionaryRoot.Remove(theme.Dictionary);
                            if (s_lastResources.Contains(theme.Dictionary))
                                s_lastResources.Remove(theme.Dictionary);
                        }
                    });
                }
            }
        }

        /// <summary>
        /// Sets the theme asynchronous.
        /// </summary>
        /// <param name="theme">The theme.</param>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static async Task SetThemeAsync(string theme = null, CancellationToken token = default(CancellationToken))
        {
            using (s_locker.LockRead())
            {
                if (!string.IsNullOrEmpty(CurrentTheme) &&
                    !string.IsNullOrEmpty(theme) &&
                    string.Equals(CurrentTheme, theme, StringComparison.CurrentCultureIgnoreCase))
                    return;

                await DispatchHelper.Instance.DispatchExecAsync((e) =>
                {
                    var themes = s_themes.Where(t =>
                    (string.IsNullOrEmpty(theme) && string.IsNullOrEmpty(t.Value.ThemeName)) ||
                    (string.Equals(t.Value.ThemeName, theme, StringComparison.CurrentCultureIgnoreCase))).ToArray();
                    if (themes != null && themes.Length > 0)
                    {
                        var oldTheme = CurrentTheme;
                        var oldDir = s_lastResources.ToArray();
                        var toKeep = (from t in themes
                                      where oldDir.Contains(t.Value.Dictionary)
                                      select t.Value).ToArray();
                        var toAdd = themes.Select(t => t.Value).Except(toKeep).ToArray();
                        var toRemove = (from t in oldDir
                                        let th = themes.FirstOrDefault(s => s.Value.Dictionary == t)
                                        where th.Key == Guid.Empty
                                        select t).ToArray();

                        foreach (var r in toRemove)
                        {
                            s_dictionaryRoot.Remove(r);
                        }

                        foreach (var a in toAdd)
                        {
                            s_dictionaryRoot.Add(a.Dictionary);
                        }

                        s_lastResources.Clear();
                        s_lastResources.AddRange(themes.Select(t => t.Value.Dictionary));
                    }
                    s_currentTheme = theme;
                }, token);
            }
        }

        /// <summary>
        /// Clears this instance.
        /// </summary>
        public static void Reset()
        {
            using (s_locker.LockWrite())
            {
                s_themes.Clear();
                s_currentTheme = null;
            }
        }

        #endregion
    }
}
